/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once

#include <baja/graphics/dx11.1/details/common.hpp>
#include <baja/graphics/dx11.1/graphics_fwd.hpp>
#include <baja/graphics/dx11.1/details/enum_mappings.hpp>

#include <baja/graphics/dx11.1/details/texture2d_impl.hpp>
#include <baja/graphics/dx11.1/details/util.hpp>

namespace baja { namespace graphics { namespace dx11_1 {

namespace d = baja::graphics::dx11_1::details;

// surface implementation
class surface : public isurface, public b::iunknown_provider
{
    std::shared_ptr<d::texture2d> _texture;

public:
    surface(const std::shared_ptr<d::texture2d>& texture) : _texture(texture) {}
   
    static std::shared_ptr<surface> create(const std::shared_ptr<d::texture2d>& texture)
    {
        std::shared_ptr<surface> returnValue = std::make_shared<surface>(texture);
        return returnValue;
    }

    // b::iunknown_provider
    
    virtual size_t getInterfaceCount()
    {
        return _texture->getInterfaceCount();
    }

    virtual WRL::ComPtr<IUnknown> getInterface(size_t index)
    {
        return _texture->getInterface(index);
    }

    // isurface
    
    virtual uint32 getWidth()
    {
        return _texture->getWidth();
    }
    
    virtual uint32 getHeight()
    {
        return _texture->getHeight();
    }
    
    virtual pixel_format getPixelFormat()
    {
        return _texture->getPixelFormat();
    }

    virtual uint32 getBitsPerPixel()
    {
        return _texture->getBitsPerPixel();
    }

    virtual void lock(const lock_type lockType, memory** data, uint32* pitch)
    {
        return _texture->lock(0, lockType, data, pitch);
    }
    
    virtual void unlock()
    {
        return _texture->unlock();
    }

    /*
    // NYI:
    virtual void  SaveToFile(const wchar* file, GxFileFormatEnum format)
    {
        return _texture->SaveToFile(file, format);
    }

    virtual void  SaveToStream(IStream* toStream, GxFileFormatEnum format)
    {
        return _texture->SaveToStream(toStream, format);
    }
    */
};

// isurface factory methods

// creates a surface from the specified view
inline std::shared_ptr<isurface> createSurfaceFromView(ID3D11View* view, const texture_type type)
{
    BAJA_CHECK_ARG(view != nullptr);

    // wrap the view into texture2d object
    std::shared_ptr<d::texture2d> textureWrapper = d::texture2d::create(view, type);

    // create new surface object
    std::shared_ptr<isurface> newSurface = surface::create(textureWrapper);

    return newSurface;
}

// Creates a depth stencil surface
inline std::shared_ptr<isurface>  
engine::createDepthStencilSurface( 
    const uint32 width,
    const uint32 height,
    const pixel_format format,
    const bool useFSAA
    )
{ 
    // create new texture
    WRL::ComPtr<ID3D11Texture2D> newTexture;
    newTexture = d::createTexture2DObject(
        _d3d11Device.Get(),
        width,
        height,
        1,
        format,
        (useFSAA ? _msaaCount : 1),
        (useFSAA ? _msaaQualityLevel : 0),
        D3D11_USAGE_DEFAULT,
        D3D11_BIND_DEPTH_STENCIL,
        0,
        0);

    win32_result wr;

    // create view on texture
    WRL::ComPtr<ID3D11DepthStencilView> view;
    wr = _d3d11Device->CreateDepthStencilView(newTexture.Get(), NULL, view.GetAddressOf());

    // wrap the view into a surface
    return createSurfaceFromView(view.Get(), texture_type::depthStencil);
}


// Creates an offscreen surface
inline std::shared_ptr<isurface>  
engine::createOffscreenSurface( 
    const uint32 width,
    const uint32 height,
    const pixel_format format
    )
{ 
    // create new texture
    WRL::ComPtr<ID3D11Texture2D> newTexture;
    newTexture = d::createTexture2DObject(
        _d3d11Device.Get(),
        width,
        height,
        1,
        format,
        1,
        0,
        D3D11_USAGE_STAGING,
        0,
        D3D11_CPU_ACCESS_READ|D3D11_CPU_ACCESS_WRITE,
        0
        );

    win32_result wr;

    // create view on texture
    WRL::ComPtr<ID3D11UnorderedAccessView> view;
    wr = _d3d11Device->CreateUnorderedAccessView(newTexture.Get(), NULL, view.GetAddressOf());

    // wrap the view into a surface
    return createSurfaceFromView(view.Get(), texture_type::undorderedAccess);
}

// Creates a render target surface
inline std::shared_ptr<isurface>  
engine::createRenderTarget( 
    const uint32 width,
    const uint32 height,
    const pixel_format format,
    bool useFSAA
    )
{ 
    // create new texture
    WRL::ComPtr<ID3D11Texture2D> newTexture;
    newTexture = d::createTexture2DObject(
        _d3d11Device.Get(),
        width,
        height,
        1,
        format,
        (useFSAA ? _msaaCount : 1),
        (useFSAA ? _msaaQualityLevel : 0),
        D3D11_USAGE_DEFAULT,
        D3D11_BIND_RENDER_TARGET,
        0,
        0
        );

    win32_result wr;

    // create view on texture
    WRL::ComPtr<ID3D11RenderTargetView> view;
    wr = _d3d11Device->CreateRenderTargetView(newTexture.Get(), NULL, view.GetAddressOf());

    // wrap the view into a surface
    return createSurfaceFromView(view.Get(), texture_type::renderTarget);
}

}}} // namespace baja.graphics.dx11_1
